﻿using System;
using System.Net;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
using System.ServiceModel.Syndication;
using System.IO;
using ajayumi.develop.win32.GRssReader.Service;
using System.Text;
using ajayumi.develop.win32.GRssReader.Service.Model;

namespace ajayumi.develop.win32.GRssReader.Google.Reader
{
    public class ReaderService : GoogleService, IReaderService
    {

        /// <summary>
        /// Current username.
        /// </summary>
        private string username;

        public SyndicationFeed Feed
        {
            get;
            set;
        }

        /// <summary>
        /// Initialize the Google reader.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="source"></param>
        public ReaderService(string username, string password, string source)
            : base("reader", username, password, source)
        {
            this.username = username;
            this.IsLogin = base.IsLogin;
        }

        //#region Feed
        ///// <summary>
        ///// Get the contents of a feed.
        ///// </summary>
        ///// <param name="feedUrl">
        ///// Must be exact URL of the feed, ex: http://sandrinodimattia.net/blog/syndication.axd
        ///// </param>
        ///// <param name="limit"></param>
        ///// <returns></returns>
        //public IEnumerable<ArticleInfo> GetFeedContent(string feedUrl, int limit)
        //{
        //    try
        //    {
        //        return GetItemsFromFeed(String.Format("{0}{1}", ReaderUrl.FeedUrl, System.Uri.EscapeDataString(feedUrl)), limit);
        //    }
        //    catch (WebException wex)
        //    {
        //        HttpWebResponse rsp = wex.Response as HttpWebResponse;
        //        if (rsp != null && rsp.StatusCode == HttpStatusCode.NotFound)
        //            throw new FeedNotFoundException(feedUrl);
        //        else
        //            throw;
        //    }
        //}
        //#endregion

        #region Subscription
        /// <summary>
        /// Subscribe to a feed.
        /// </summary>
        /// <param name="feed"></param>
        public void AddSubscription(string feed)
        {
            PostRequest(ReaderCommand.SubscriptionAdd, new GoogleParameter("quickadd", feed));
        }

        /// <summary>
        /// Tag a subscription (remove it).
        /// </summary>
        /// <param name="feed"></param>
        /// <param name="folder"></param>
        public void TagSubscription(string feed, string folder)
        {
            PostRequest(ReaderCommand.SubscriptionEdit,
                new GoogleParameter("a", ReaderUrl.LabelPath + folder),
                new GoogleParameter("s", "feed/" + feed),
                new GoogleParameter("ac", "edit"));
        }


        public List<SubscriptionInfo> GetSubscriptionInfos()
        {
            // Get the XML for subscriptions.
            string xml = session.GetSource(ReaderCommand.SubscriptionList.GetFullUrl());

            // Get a list of subscriptions.
            return XElement.Parse(xml).Elements("list").Elements("object").Select(o => new SubscriptionInfo(o)).ToList();
        }

        public List<UnReadItem> GetUnReadItems()
        {
            // Get the XML for subscriptions.
            string xml = session.GetSource(ReaderCommand.UnreadCount.GetFullUrl(),
                new GoogleParameter("all", "true"));

            // Get a list of subscriptions.
            List<UnReadItem> lst = XElement.Parse(xml).Elements("list").Elements("object").Select(o => new UnReadItem(o)).ToList();
            return lst;
        }

        private static string GetLocalXml(string path)
        {
            string xml = string.Empty;
            using (StreamReader sr = new StreamReader(path))
            {
                xml = sr.ReadToEnd();
            }
            return xml;
        }
        #endregion

        #region Tags
        /// <summary>
        /// Add tags to an item.
        /// </summary>
        /// <param name="feed"></param>
        /// <param name="folder"></param>
        public void AddTags(ArticleInfo item, params string[] tags)
        {
            // Calculate the amount of parameters required.
            int arraySize = tags.Length + item.CategoryList.Count + 2;

            // Set all parameters.
            GoogleParameter[] parameters = new GoogleParameter[arraySize];
            parameters[0] = new GoogleParameter("s", "feed/" + item.FeedInfo.Url);
            parameters[1] = new GoogleParameter("i", item.Id);

            int nextParam = 2;

            // Add parameters.
            for (int i = 0; i < item.CategoryList.Count; i++)
                parameters[nextParam++] = new GoogleParameter("a", ReaderUrl.LabelPath + item.CategoryList[i]);
            for (int i = 0; i < tags.Length; i++)
                parameters[nextParam++] = new GoogleParameter("a", ReaderUrl.LabelPath + tags[i]);

            // Send request.
            PostRequest(ReaderCommand.TagAdd, parameters);
        }

        /// <summary>
        /// Rename a tag.
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="newName"></param>
        public void RenameTag(string tag, string newName)
        {
            PostRequest(ReaderCommand.TagRename,
                new GoogleParameter("s", ReaderUrl.LabelPath + tag),
                new GoogleParameter("t", tag),
                new GoogleParameter("dest", ReaderUrl.LabelPath + newName));
        }

        /// <summary>
        /// Remove tag (for all items).
        /// </summary>
        /// <param name="tag"></param>
        public void RemoveTag(string tag)
        {
            PostRequest(ReaderCommand.TagDelete,
                new GoogleParameter("s", ReaderUrl.LabelPath + tag),
                new GoogleParameter("t", tag));
        }

        /// <summary>
        /// Remove tag from a single item.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="tag"></param>
        public void RemoveTag(ArticleInfo item, string tag)
        {
            PostRequest(ReaderCommand.TagEdit,
                new GoogleParameter("r", ReaderUrl.LabelPath + tag),
                new GoogleParameter("s", "feed/" + item.FeedInfo.Url),
                new GoogleParameter("i", item.Id));
        }

        /// <summary>
        /// Get a list of tags.
        /// </summary>
        /// <returns></returns>
        public List<string> GetTags()
        {
            string xml = session.GetSource(ReaderCommand.TagList.GetFullUrl());

            // Get the list of tags.
            var tagElements = from t in XElement.Parse(xml).Elements("list").Descendants("string")
                              where t.Attribute("name").Value == "id"
                              where t.Value.Contains("/label/")
                              select t;

            // Create a list.
            List<string> tags = new List<string>();
            foreach (XElement element in tagElements)
            {
                string tag = element.Value.Substring(element.Value.LastIndexOf('/') + 1,
                    element.Value.Length - element.Value.LastIndexOf('/') - 1);
                tags.Add(tag);
            }

            // Done.
            return tags;
        }

        /// <summary>
        /// Get all items for a tag.
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public IEnumerable<ArticleInfo> GetTagItems(string tag, int limit)
        {
            string url = String.Format("{0}{1}",
                ReaderUrl.LabelPath, System.Uri.EscapeDataString(tag));
            return GetItemsFromFeed(url, limit);
        }
        #endregion

        #region State
        /// <summary>
        /// Add state for an item.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="state"></param>
        public void AddState(ArticleInfo item, State state)
        {
            PostRequest(ReaderCommand.TagEdit,
                new GoogleParameter("a", ReaderUrl.StatePath + StateFormatter.ToString(state)),
                new GoogleParameter("i", item.Id),
                new GoogleParameter("s", "feed/" + item.FeedInfo.Url));
        }

        /// <summary>
        /// Remove a state from an item.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="state"></param>
        public void RemoveState(ArticleInfo item, State state)
        {
            PostRequest(ReaderCommand.TagEdit,
                new GoogleParameter("r", ReaderUrl.StatePath + StateFormatter.ToString(state)),
                new GoogleParameter("i", item.Id),
                new GoogleParameter("s", "feed/" + item.FeedInfo.Url));
        }

        /// <summary>
        /// Get the content of a state. 
        /// For example: Get all starred items.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public IEnumerable<ArticleInfo> GetStateItems(State state, int limit)
        {
            string url = String.Format("{0}{1}", ReaderUrl.StateUrl, StateFormatter.ToString(state));
            return GetItemsFromFeed(url, limit);
        }
        #endregion

        /// <summary>
        /// Post a request using a reader command.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="postFields"></param>
        private void PostRequest(ReaderCommand command, params GoogleParameter[] postFields)
        {
            session.PostRequest(ReaderCommandFormatter.GetFullUrl(command), postFields);
        }

        /// <summary>
        /// Get items from a feed and convert them to a GoogleReaderItem.
        /// </summary>
        /// <param name="url"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        private IEnumerable<ArticleInfo> GetItemsFromFeed(string url, int limit)
        {
            this.Feed = session.GetFeed(url, new GoogleParameter("n", limit.ToString()));

            return this.Feed.Items.Select<SyndicationItem, ArticleInfo>(o => new ArticleInfo(o));
        }



        public List<SubscriptionInfo> GetSubscriptionInfoList()
        {
            List<SubscriptionInfo> subscriptionList = new List<SubscriptionInfo>();
            List<string> tags = this.GetTags();
            List<SubscriptionInfo> subscriptionInfos = this.GetSubscriptionInfos();
            List<UnReadItem> unreadItems = this.GetUnReadItems();

            foreach (string tag in tags)
            {
                IEnumerable<SubscriptionInfo> subs = from o in subscriptionInfos
                                                     where o.CategoryList.Contains(tag)
                                                     select o;
                foreach (SubscriptionInfo subInfo in subs)
                {
                    SubscriptionInfo sub = new SubscriptionInfo(null) { Category = tag };
                    sub = subInfo;
                    IEnumerable<UnReadItem> subUnreadItems = from o in unreadItems
                                                             where o.Id == subInfo.Id
                                                             select o;
                    if (subUnreadItems != null && subUnreadItems.Count() > 0 && subUnreadItems.First() != null)
                    {
                        sub.UnReadCount = subUnreadItems.First().Count;
                    }
                    subscriptionList.Add(sub);
                }
            }

            // 保存离线 Subscription 数据
            string path = Path.Combine(this.CacheFolder, Service.Constants.SubscriptionFileName);
            XmlWriterSettings xmlWriterSettings = new XmlWriterSettings()
            {
                Indent = true,
                Encoding = Encoding.UTF8,
                CloseOutput = true
            };
            using (XmlWriter xmlWriter = XmlWriter.Create(path, xmlWriterSettings))
            {
                xmlWriter.WriteStartDocument();
                xmlWriter.WriteStartElement("object");
                xmlWriter.WriteStartElement("list");
                xmlWriter.WriteAttributeString("name", "subscriptions");
                foreach (SubscriptionInfo _SubscriptionInfo in subscriptionList)
                {
                    _SubscriptionInfo.SaveItem(xmlWriter);
                }
                xmlWriter.WriteEndElement();
                xmlWriter.WriteEndElement();
                xmlWriter.WriteEndDocument();
            }

            return subscriptionList;
        }


        public void Update()
        {
            //throw new NotImplementedException();
        }

        public IEnumerable<ArticleInfo> GetArticleInfos(SubscriptionInfo subscriptionInfo, int limit)
        {
            string feedUrl = subscriptionInfo.Url;
            try
            {
                string url = String.Format("{0}{1}", ReaderUrl.FeedUrl, System.Uri.EscapeDataString(feedUrl));
                return this.GetItemsFromFeed(url, limit);
            }
            catch (WebException ex)
            {
                HttpWebResponse rsp = ex.Response as HttpWebResponse;
                if (rsp != null && rsp.StatusCode == HttpStatusCode.NotFound)
                    throw new FeedNotFoundException(feedUrl);
                else
                    throw;
            }
        }





    }
}
